package com.lineage.server.datatables;

import com.lineage.DatabaseFactory;
import com.lineage.config.ConfigBoxs;
import com.lineage.server.model.Instance.L1ItemInstance;
import com.lineage.server.model.Instance.L1PcInstance;
import com.lineage.server.serverpackets.S_ServerMessage;
import com.lineage.server.serverpackets.ServerBasePacket;
import com.lineage.server.templates.L1Box;
import com.lineage.server.templates.L1Item;
import com.lineage.server.thread.GeneralThreadPool;
import com.lineage.server.utils.PerformanceTimer;
import com.lineage.server.utils.SQLUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class ItemBoxTable {
  private static final Log _log = LogFactory.getLog(ItemBoxTable.class);
  
  private static final Random _random = new Random();
  
  private static final Map<Integer, ArrayList<L1Box>> _box = new HashMap<>();
  
  private static final Map<Integer, ArrayList<L1Box>> _boxs = new HashMap<>();
  
  private static final Map<Integer, HashMap<Integer, ArrayList<L1Box>>> _boxkey = new HashMap<>();
  
  private static ItemBoxTable _instance;
  
  public static ItemBoxTable get() {
    if (_instance == null)
      _instance = new ItemBoxTable(); 
    return _instance;
  }
  
  public static void reload() {
    _instance = new ItemBoxTable();
    _box.clear();
    _boxs.clear();
    _boxkey.clear();
    _instance.load();
  }
  
  public void reload1() {
    _box.clear();
    _boxs.clear();
    _boxkey.clear();
    load();
  }
  
  private static void updata_name(int key, int itemId) {
    Connection cn = null;
    PreparedStatement ps = null;
    String boxname = ItemTable.get().getTemplate(key).getName();
    String itemname = ItemTable.get().getTemplate(itemId).getName();
    try {
      cn = DatabaseFactory.get().getConnection();
      ps = cn.prepareStatement("UPDATE `etcitem_box` SET `name`=? WHERE `box_item_id`=? AND `get_item_id`=?");
      int i = 0;
      ps.setString(++i, String.valueOf(String.valueOf(boxname)) + "=>" + itemname);
      ps.setInt(++i, key);
      ps.setInt(++i, itemId);
      ps.execute();
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(ps);
      SQLUtil.close(cn);
    } 
  }
  
  private static void updata_name2(int key, int itemId) {
    Connection cn = null;
    PreparedStatement ps = null;
    String boxname = ItemTable.get().getTemplate(key).getName();
    String itemname = ItemTable.get().getTemplate(itemId).getName();
    try {
      cn = DatabaseFactory.get().getConnection();
      ps = cn.prepareStatement("UPDATE `etcitem_boxs` SET `name`=? WHERE `box_item_id`=? AND `get_item_id`=?");
      int i = 0;
      ps.setString(++i, String.valueOf(String.valueOf(boxname)) + "=>" + itemname);
      ps.setInt(++i, key);
      ps.setInt(++i, itemId);
      ps.execute();
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(ps);
      SQLUtil.close(cn);
    } 
  }
  
  private static void updata_name3(int key, int itemId) {
    Connection cn = null;
    PreparedStatement ps = null;
    String boxname = ItemTable.get().getTemplate(key).getName();
    String itemname = ItemTable.get().getTemplate(itemId).getName();
    try {
      cn = DatabaseFactory.get().getConnection();
      ps = cn.prepareStatement("UPDATE `etcitem_box_key` SET `name`=? WHERE `box_item_id`=? AND `get_item_id`=?");
      int i = 0;
      ps.setString(++i, String.valueOf(String.valueOf(boxname)) + "=>" + itemname);
      ps.setInt(++i, key);
      ps.setInt(++i, itemId);
      ps.execute();
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(ps);
      SQLUtil.close(cn);
    } 
  }
  
  private static void outItem(L1PcInstance pc, L1Box box, String tgitemName) {
    if (box != null) {
      L1Item temp = ItemTable.get().getTemplate(box.get_item_id());
      int count = 1;
      if (box.get_min_count() < box.get_max_count()) {
        count = _random.nextInt(box.get_max_count() - box.get_min_count()) + box.get_min_count();
      } else {
        count = box.get_min_count();
      } 
      int enchantlvl = box.get_enchantlvl();
      L1ItemInstance item = null;
      if (temp.isStackable()) {
        item = ItemTable.get().createItem(box.get_item_id());
        item.setCount(count);
        if (enchantlvl > 0)
          item.setEnchantLevel(enchantlvl); 
        createNewItem(pc, item);
      } else {
        int i = 0;
        while (i < count) {
          item = ItemTable.get().createItem(box.get_item_id());
          if (enchantlvl > 0)
            item.setEnchantLevel(enchantlvl); 
          createNewItem(pc, item);
          i++;
        } 
      } 
      String itemName = item.getName();
      if (item != null && box.is_out() && !pc.isGm())
        ConfigBoxs.msg(pc.getName(), tgitemName, String.valueOf(String.valueOf(itemName)) + "(" + item.getCount() + "}"); 
      RecordTable.get().recordbox(pc.getName(), tgitemName, itemName, count);
    } 
  }
  
  private static void createNewItem(L1PcInstance pc, L1ItemInstance item) {
    try {
      pc.getInventory().storeItem(item);
      pc.sendPackets((ServerBasePacket)new S_ServerMessage(403, item.getLogName()));
      pc.saveInventory();
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
    } 
  }
  
  public void load() {
    load_box();
    load_boxs();
    load_box_key();
  }
  
  public void load_box() {
    PerformanceTimer timer = new PerformanceTimer();
    Connection con = null;
    PreparedStatement pstm = null;
    ResultSet rs = null;
    int i = 0;
    try {
      con = DatabaseFactory.get().getConnection();
      pstm = con.prepareStatement("SELECT * FROM `etcitem_box`");
      rs = pstm.executeQuery();
      while (rs.next()) {
        int key = rs.getInt("box_item_id");
        L1Item temp = ItemTable.get().getTemplate(key);
        if (temp == null) {
          del_box(key);
          continue;
        } 
        if (temp.getType() != 16)
          continue; 
        int get_item_id = rs.getInt("get_item_id");
        String note = rs.getString("name");
        L1Item tempget = ItemTable.get().getTemplate(key);
        if (tempget == null) {
          del_box2(key);
          continue;
        } 
        L1Box box = new L1Box();
        box.set_box_item_id(key);
        box.set_get_item_id(get_item_id);
        box.set_randomint(rs.getInt("randomint"));
        box.set_random(rs.getInt("random"));
        box.set_min_count(rs.getInt("min_count"));
        box.set_max_count(rs.getInt("max_count"));
        box.set_out(rs.getBoolean("out"));
        box.set_enchantlvl(rs.getInt("enchantlvl"));
        ArrayList<L1Box> list = _box.get(Integer.valueOf(key));
        if (list == null)
          list = new ArrayList<>(); 
        list.add(box);
        _box.put(Integer.valueOf(key), list);
        i++;
        if (note.contains("=>"))
          continue; 
        updata_name(key, get_item_id);
      } 
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(rs);
      SQLUtil.close(pstm);
      SQLUtil.close(con);
    } 
    _log.info("载入箱子开出物设置: " + _box.size() + "/" + i + "(" + timer.get() + "ms)");
  }
  
  private void del_box(int id) {
    Connection cn = null;
    PreparedStatement ps = null;
    try {
      cn = DatabaseFactory.get().getConnection();
      ps = cn.prepareStatement("DELETE FROM `etcitem_box` WHERE `box_item_id`=?");
      ps.setInt(1, id);
      ps.execute();
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(ps);
      SQLUtil.close(cn);
    } 
  }
  
  private void del_box2(int get_item_id) {
    Connection cn = null;
    PreparedStatement ps = null;
    try {
      cn = DatabaseFactory.get().getConnection();
      ps = cn.prepareStatement("DELETE FROM `etcitem_box` WHERE `get_item_id`=?");
      ps.setInt(1, get_item_id);
      ps.execute();
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(ps);
      SQLUtil.close(cn);
    } 
  }
  
  private void load_boxs() {
    PerformanceTimer timer = new PerformanceTimer();
    Connection con = null;
    PreparedStatement pstm = null;
    ResultSet rs = null;
    int i = 0;
    try {
      con = DatabaseFactory.get().getConnection();
      pstm = con.prepareStatement("SELECT * FROM `etcitem_boxs`");
      rs = pstm.executeQuery();
      while (rs.next()) {
        int key = rs.getInt("box_item_id");
        L1Item temp = ItemTable.get().getTemplate(key);
        if (temp == null) {
          del_boxs(key);
          continue;
        } 
        if (temp.getType() != 16)
          continue; 
        int get_item_id = rs.getInt("get_item_id");
        String note = rs.getString("name");
        L1Item tempget = ItemTable.get().getTemplate(key);
        if (tempget == null) {
          del_boxs2(key);
          continue;
        } 
        L1Box box = new L1Box();
        box.set_box_item_id(key);
        box.set_get_item_id(get_item_id);
        box.set_randomint(100);
        box.set_random(-1);
        int count = rs.getInt("count");
        box.set_min_count(count);
        box.set_max_count(count);
        box.set_use_type(rs.getInt("use_type"));
        box.set_enchantlvl(rs.getInt("enchantlvl"));
        box.set_out(rs.getBoolean("out"));
        ArrayList<L1Box> list = _boxs.get(Integer.valueOf(key));
        if (list == null)
          list = new ArrayList<>(); 
        list.add(box);
        _boxs.put(Integer.valueOf(key), list);
        i++;
        if (note.contains("=>"))
          continue; 
        updata_name2(key, get_item_id);
      } 
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(rs);
      SQLUtil.close(pstm);
      SQLUtil.close(con);
    } 
    _log.info("载入箱子开出物设置(多种): " + _boxs.size() + "/" + i + "(" + timer.get() + "ms)");
  }
  
  private void del_boxs(int id) {
    Connection cn = null;
    PreparedStatement ps = null;
    try {
      cn = DatabaseFactory.get().getConnection();
      ps = cn.prepareStatement("DELETE FROM `etcitem_boxs` WHERE `box_item_id`=?");
      ps.setInt(1, id);
      ps.execute();
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(ps);
      SQLUtil.close(cn);
    } 
  }
  
  private void del_boxs2(int get_item_id) {
    Connection cn = null;
    PreparedStatement ps = null;
    try {
      cn = DatabaseFactory.get().getConnection();
      ps = cn.prepareStatement("DELETE FROM `etcitem_boxs` WHERE `get_item_id`=?");
      ps.setInt(1, get_item_id);
      ps.execute();
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(ps);
      SQLUtil.close(cn);
    } 
  }
  
  private void load_box_key() {
    PerformanceTimer timer = new PerformanceTimer();
    Connection con = null;
    PreparedStatement pstm = null;
    ResultSet rs = null;
    int i = 0;
    try {
      con = DatabaseFactory.get().getConnection();
      pstm = con.prepareStatement("SELECT * FROM `etcitem_box_key`");
      rs = pstm.executeQuery();
      while (rs.next()) {
        int keyId = rs.getInt("key_itemid");
        L1Item tempkey = ItemTable.get().getTemplate(keyId);
        if (tempkey == null) {
          del_box_key(keyId);
          continue;
        } 
        int key = rs.getInt("box_item_id");
        String note = rs.getString("name");
        L1Item temp = ItemTable.get().getTemplate(key);
        if (temp == null) {
          del_box_key2(key);
          continue;
        } 
        if (temp.getType() != 16)
          continue; 
        int get_item_id = rs.getInt("get_item_id");
        L1Item tempget = ItemTable.get().getTemplate(get_item_id);
        if (tempget == null) {
          del_box_key3(get_item_id);
          continue;
        } 
        L1Box box = new L1Box();
        box.set_box_item_id(key);
        box.set_get_item_id(get_item_id);
        box.set_randomint(rs.getInt("randomint"));
        box.set_random(rs.getInt("random"));
        box.set_min_count(rs.getInt("min_count"));
        box.set_max_count(rs.getInt("max_count"));
        box.set_out(rs.getBoolean("out"));
        box.set_use_type(127);
        box.set_enchantlvl(rs.getInt("enchantlvl"));
        HashMap<Integer, ArrayList<L1Box>> map = _boxkey.get(Integer.valueOf(key));
        if (map == null)
          map = new HashMap<>(); 
        ArrayList<L1Box> keylist = map.get(Integer.valueOf(keyId));
        if (keylist == null)
          keylist = new ArrayList<>(); 
        keylist.add(box);
        map.put(Integer.valueOf(keyId), keylist);
        _boxkey.put(Integer.valueOf(key), map);
        i++;
        if (note.contains("=>"))
          continue; 
        updata_name3(key, get_item_id);
      } 
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(rs);
      SQLUtil.close(pstm);
      SQLUtil.close(con);
    } 
    _log
      .info("载入箱子开出物设置(指定使用物品开启): " + _boxkey.size() + "/" + i + "(" + timer.get() + "ms)");
  }
  
  private void del_box_key(int key_itemid) {
    Connection cn = null;
    PreparedStatement ps = null;
    try {
      cn = DatabaseFactory.get().getConnection();
      ps = cn.prepareStatement("DELETE FROM `etcitem_box_key` WHERE `key_itemid`=?");
      ps.setInt(1, key_itemid);
      ps.execute();
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(ps);
      SQLUtil.close(cn);
    } 
  }
  
  private void del_box_key2(int box_item_id) {
    Connection cn = null;
    PreparedStatement ps = null;
    try {
      cn = DatabaseFactory.get().getConnection();
      ps = cn.prepareStatement("DELETE FROM `etcitem_box_key` WHERE `box_item_id`=?");
      ps.setInt(1, box_item_id);
      ps.execute();
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(ps);
      SQLUtil.close(cn);
    } 
  }
  
  private void del_box_key3(int get_item_id) {
    Connection cn = null;
    PreparedStatement ps = null;
    try {
      cn = DatabaseFactory.get().getConnection();
      ps = cn.prepareStatement("DELETE FROM `etcitem_box_key` WHERE `get_item_id`=?");
      ps.setInt(1, get_item_id);
      ps.execute();
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(ps);
      SQLUtil.close(cn);
    } 
  }
  
  public ArrayList<L1Box> get(L1PcInstance pc, L1ItemInstance tgitem) {
    try {
      ArrayList<L1Box> list = _box.get(Integer.valueOf(tgitem.getItemId()));
      if (list != null) {
        BoxRandom boxs = new BoxRandom(pc, tgitem, list);
        boxs.getStart();
        return list;
      } 
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
    } 
    return null;
  }
  
  public void get_all(L1PcInstance pc, L1ItemInstance tgitem) {
    try {
      ArrayList<L1Box> list = _boxs.get(Integer.valueOf(tgitem.getItemId()));
      if (list != null) {
        if (list.size() <= 0)
          return; 
        if (list.isEmpty())
          return; 
        int MaxChargeCount = tgitem.getItem().getMaxChargeCount();
        int chargecount = tgitem.getChargeCount();
        String name = tgitem.getName();
        int newchargecount = chargecount - 1;
        if (MaxChargeCount > 0) {
          if (chargecount > 0) {
            tgitem.setChargeCount(newchargecount);
            pc.getInventory().updateItem(tgitem, 128);
            Iterator<L1Box> iterator = list.iterator();
            while (iterator.hasNext()) {
              L1Box box = iterator.next();
              if (!box.is_use(pc))
                continue; 
              outItem(pc, box, name);
            } 
          } 
          if (newchargecount <= 0)
            pc.getInventory().deleteItem(tgitem); 
        } else if (pc.getInventory().removeItem(tgitem, 1L) == 1L) {
          Iterator<L1Box> iterator2 = list.iterator();
          while (iterator2.hasNext()) {
            L1Box box = iterator2.next();
            if (!box.is_use(pc))
              continue; 
            outItem(pc, box, name);
          } 
        } 
      } else {
        pc.sendPackets((ServerBasePacket)new S_ServerMessage(79));
      } 
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
    } 
  }
  
  public boolean is_key(int tgid, int keyid) {
    HashMap<Integer, ArrayList<L1Box>> map = _boxkey.get(Integer.valueOf(tgid));
    if (map != null) {
      ArrayList<L1Box> keylist = map.get(Integer.valueOf(keyid));
      if (keylist != null)
        return true; 
    } 
    return false;
  }
  
  public void get_key(L1PcInstance pc, L1ItemInstance tgitem, int keyid) {
    try {
      HashMap<Integer, ArrayList<L1Box>> map = _boxkey
        .get(Integer.valueOf(tgitem.getItemId()));
      if (map != null) {
        ArrayList<L1Box> keylist = map.get(Integer.valueOf(keyid));
        if (keylist != null) {
          if (keylist.size() <= 0)
            return; 
          if (keylist.isEmpty())
            return; 
          BoxRandom boxs = new BoxRandom(pc, tgitem, keylist);
          boxs.getStart();
        } 
      } 
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
    } 
  }
  
  public void set_box(int box_item_id, int get_item_id, String name, int randomint, int random, int min_count, int max_count) {
    Connection cn = null;
    PreparedStatement ps = null;
    try {
      cn = DatabaseFactory.get().getConnection();
      ps = cn.prepareStatement(
          "INSERT INTO `etcitem_box` SET `box_item_id`=?,`get_item_id`=?,`name`=?,`randomint`=?,`random`=?,`min_count`=?,`max_count`=?,`out`=?");
      int i = 0;
      ps.setInt(++i, box_item_id);
      ps.setInt(++i, get_item_id);
      ps.setString(++i, name);
      ps.setInt(++i, randomint);
      ps.setInt(++i, random);
      ps.setInt(++i, min_count);
      ps.setInt(++i, max_count);
      ps.setBoolean(++i, false);
      ps.execute();
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(ps);
      SQLUtil.close(cn);
    } 
  }
  
  public void set_boxs(int box_item_id, int get_item_id, String name, int count) {
    Connection cn = null;
    PreparedStatement ps = null;
    try {
      cn = DatabaseFactory.get().getConnection();
      ps = cn.prepareStatement(
          "INSERT INTO `etcitem_boxs` SET `box_item_id`=?,`get_item_id`=?,`name`=?,`count`=?,`out`=?");
      int i = 0;
      ps.setInt(++i, box_item_id);
      ps.setInt(++i, get_item_id);
      ps.setString(++i, name);
      ps.setInt(++i, count);
      ps.execute();
    } catch (SQLException e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      SQLUtil.close(ps);
      SQLUtil.close(cn);
    } 
  }
  
  private class BoxRandom implements Runnable {
    private final L1PcInstance _pc;
    
    private final ArrayList<L1Box> _list_tmp;
    
    private final L1ItemInstance _tgitem;
    
    public BoxRandom(L1PcInstance pc, L1ItemInstance tgitem, ArrayList<L1Box> list) {
      this._list_tmp = new ArrayList<>();
      this._pc = pc;
      this._tgitem = tgitem;
      this._list_tmp.addAll(list);
    }
    
    public void getStart() {
      GeneralThreadPool.get().schedule(this, 0L);
    }
    
    public void run() {
      try {
        if (this._list_tmp.size() <= 0)
          return; 
        if (this._list_tmp.isEmpty())
          return; 
        Map<Integer, Integer> tempList = new HashMap<>();
        L1Box box = null;
        int i = 0;
        while (box == null) {
          box = runItem(tempList);
          Thread.sleep(1L);
          if (++i >= 300) {
            Object[] obj = this._list_tmp.toArray();
            box = (L1Box)obj[ItemBoxTable._random.nextInt(obj.length)];
            if (box != null)
              break; 
          } 
        } 
        if (box != null) {
          int MaxChargeCount = this._tgitem.getItem().getMaxChargeCount();
          int chargecount = this._tgitem.getChargeCount();
          String name = this._tgitem.getName();
          int newchargecount = chargecount - 1;
          if (MaxChargeCount > 0) {
            if (chargecount > 0) {
              this._tgitem.setChargeCount(newchargecount);
              this._pc.getInventory().updateItem(this._tgitem, 128);
              ItemBoxTable.outItem(this._pc, box, name);
            } 
            if (newchargecount <= 0)
              this._pc.getInventory().deleteItem(this._tgitem); 
          } else if (this._pc.getInventory().removeItem(this._tgitem, 1L) == 1L) {
            ItemBoxTable.outItem(this._pc, box, name);
          } 
        } 
      } catch (Exception e) {
        ItemBoxTable._log.error("宝盒物品设置或取率可能太低,本次开启未获得任何物品(宝盒不会被删除) 宝盒编号:" + this._tgitem.getItemId());
      } 
    }
    
    private L1Box runItem(Map<Integer, Integer> tempList) {
      try {
        if (this._list_tmp.size() <= 0)
          return null; 
        if (this._list_tmp.isEmpty())
          return null; 
        int index = ItemBoxTable._random.nextInt(this._list_tmp.size());
        L1Box score = this._list_tmp.get(index);
        int random = ItemBoxTable._random.nextInt(score.get_randomint());
        int srcrandom = score.get_random();
        if (random < srcrandom)
          return score; 
        Integer tmp = tempList.get(Integer.valueOf(score.get_item_id()));
        if (tmp != null) {
          tmp = Integer.valueOf(tmp.intValue() + 1);
          tempList.put(Integer.valueOf(score.get_item_id()), tmp);
        } else {
          tmp = Integer.valueOf(1);
          tempList.put(Integer.valueOf(score.get_item_id()), tmp);
        } 
        boolean isremove = false;
        if (srcrandom < 5000) {
          isremove = true;
        } else if (srcrandom >= 5000 && srcrandom < 10000) {
          if (tmp.intValue() > 2)
            isremove = true; 
        } else if (srcrandom >= 10000 && srcrandom < 20000) {
          if (tmp.intValue() > 4)
            isremove = true; 
        } else if (srcrandom >= 20000 && srcrandom < 40000) {
          if (tmp.intValue() > 8)
            isremove = true; 
        } else if (srcrandom >= 40000 && srcrandom < 80000) {
          if (tmp.intValue() > 16)
            isremove = true; 
        } else if (srcrandom >= 80000 && srcrandom < 160000) {
          if (tmp.intValue() > 32)
            isremove = true; 
        } else if (srcrandom >= 160000 && srcrandom < 320000 && tmp.intValue() > 64) {
          isremove = true;
        } 
        if (isremove)
          this._list_tmp.remove(score); 
      } catch (Exception e) {
        ItemBoxTable._log.error(e.getLocalizedMessage(), e);
        return null;
      } 
      return null;
    }
  }
}
